<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body>
  <script>
    // https://blog.csdn.net/cc18868876837/article/details/114918262

    function isObject(target) {
      return (typeof target === 'object' && target) || typeof target === 'function'
    }

    function deepClone(obj, map = new WeakMap()) {
      //判断obj是不是基本类型
      if (!isObject(obj)) return obj; //根据obj类型初始化结果变量

      if ([Date, RegExp].includes(obj.constructor)) {
        return new obj.constructor(obj)
      }

      if (typeof obj === 'function') {
        return new Function('return ' + obj.toString())()
      }

      if (map.get(obj)) {
        // 已存在则直接返回
        return map.get(obj);
      }

      // MAP
      if (obj instanceof Map) {
        const result = new Map()
        map.set(obj, result)
        obj.forEach((val, key) => {
          if (isObject(val)) {
            result.set(key, deepClone(val))
          } else {
            result.set(key, val)
          }
        })
        return result
      }

      // SET
      if (obj instanceof Set) {
        const result = new Set()
        map.set(obj, result)
        obj.forEach(val => {
          if (isObject(val)) {
            result.add(deepClone(val))
          } else {
            result.add(val)
          }
        })
        return result
      }

      let target = Array.isArray(obj) ? [] : {};

      const keys = Reflect.ownKeys(obj) //包括可枚举类型和不可枚举类型（Symbol） Object.keys只有可枚举类型

      // 不存在则第一次设置
      map.set(obj, target);

      keys.forEach(i => {
        if (isObject(obj[i])) {
          //判断数据i的类型
          // 👇传递map
          target[i] = deepClone(obj[i], map);
        } else {
          target[i] = obj[i];
        }
      })

      return target;
    }
    const obj = {
      info: { c: { d: 1 } },
      age: undefined,
      func: function () {
        console.log("Hello World");
        return 2;
      },
      key: Symbol("一个独一无二的key"),
      array: [1, 2, 3],
    };
    // 形成环引用
    obj.loop = obj;
    obj.func = function () {
      console.log("wc");
      let i = 0;
    };
    console.log(obj);
    const resultA = deepClone(obj);
    console.log(resultA); // 拷贝成功
  </script>
</body>

</html>